﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Threading;
using ArtheaEngine;
using Microsoft.Win32;
using NLog;
using Thought.Net.Telnet;

namespace ArtheaServer
{
    [Serializable]
    public class Server
    {
        private static readonly string baseKey = "Software\\" + Assembly.GetExecutingAssembly().GetName(false).Name;
        private static readonly Logger log = LogManager.GetCurrentClassLogger();
        private TelnetListener listener;
        private Thread listenThread;

        static Server()
        {
            Instance = new Server();
        }

        private Server()
        {
            RegistryKey key = Registry.CurrentUser.OpenSubKey(baseKey);

            if (key == null)
            {
                // setup
                key = Registry.CurrentUser.CreateSubKey(baseKey);
            }

            Port = Convert.ToInt16(key.GetValue("Port", 3778));
            World.Flags = Convert.ToInt32(key.GetValue("Flags", 0));
            World.CharUpdatePulse = Convert.ToInt32(key.GetValue("CharUpdatePulse", 40));
            World.AreaUpdatePulse = Convert.ToInt32(key.GetValue("AreaUpdatePulse", 40));
        }

        public static Server Instance { get; private set; }

        public static short Port { get; set; }

        public ServerState State { get; set; }

        ~Server()
        {
            RegistryKey key = Registry.CurrentUser.OpenSubKey(baseKey, true);

            if (key == null)
            {
                key = Registry.CurrentUser.CreateSubKey(baseKey);
            }

            key.SetValue("Port", Port, RegistryValueKind.DWord);
            key.SetValue("Flags", World.Flags, RegistryValueKind.DWord);
            key.SetValue("CharUpdatePulse", World.CharUpdatePulse, RegistryValueKind.DWord);
            key.SetValue("AreaUpdatePulse", World.AreaUpdatePulse, RegistryValueKind.DWord);
        }

        public void Start()
        {
            listenThread = new Thread(Listen);
            listenThread.Start();
        }

        public void Listen()
        {
            State = ServerState.Initializing;

            log.Info("Starting " + World.Name);

            World.Initialize();

            listener = new TelnetListener(IPAddress.Any, Port);

            listener.Start();

            log.Info("Accepting connections on port " + Port);

            State = ServerState.Running;

            while (State == ServerState.Running)
            {
                try
                {
                    TelnetClient client = listener.Accept();

                    var connection = new Connection(client);

                    log.Info("Got " + client.Parser.Socket.RemoteEndPoint);

                    new Thread(connection.Run) {IsBackground = true}.Start();
                }
                catch (SocketException)
                {
                }
            }
        }

        public void Stop()
        {
            log.Info("Stopping server...");

            World.DataContext.SubmitChanges();
            State = ServerState.Stopped;

            if (listener != null)
            {
                listener.Stop();
                listener = null;
            }

            Thread.Sleep(500);

            if (listenThread != null && listenThread.IsAlive)
            {
                listenThread.Abort();
            }
        }
    }
}